
package rendi456.net;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.ListIterator;

public class Server
{
	private final ServerSocket serverSocket;
	private final ConnectionListener connectionListener;
	private final ArrayList<ServerChannel> clientList;

	public Server(int port) throws IOException
	{
		this.clientList = new ArrayList<ServerChannel>();
		this.serverSocket = new ServerSocket(port);
		this.connectionListener = new ConnectionListener();
	}

	public void listen()
	{
		Server.startService(this.connectionListener);
	}

	private void welcomeSocket(Socket socket)
	{
		SocketWelcomer welcomer = null;

		welcomer = new SocketWelcomer(socket);

		Server.startService(welcomer);
	}

	private boolean addServerChannel(ServerChannel cl)
	{
		boolean ok = true;

		synchronized (this.clientList)
		{
			if (ok)
			{
				this.clientList.add(cl);
			}
		}

		return ok;
	}

	public void broadcast(Object o)
	{
		ListIterator<ServerChannel> li = null;
		ServerChannel sc = null;

		synchronized (this.clientList)
		{
			li = this.clientList.listIterator();
			while (li.hasNext())
			{
				try
				{
					sc = li.next();
					sc.feed(o);
				}
				catch (IOException e)
				{
				}
			}
		}
	}

	private class SocketWelcomer implements Runnable
	{
		private Socket socket = null;

		public SocketWelcomer(Socket socket)
		{
			this.socket = socket;
		}

		@Override
		public void run()
		{
			ServerChannel serverChannel = new ServerChannel(this.socket);
			boolean reponse = true;

			try
			{
				serverChannel.open();
				reponse = Server.this.addServerChannel(serverChannel);
				serverChannel.listen(reponse);

			}
			catch (IOException e)
			{

			}

		}

		@Override
		public String toString()
		{
			return "#WELCOMER" + Test.getThreadName() + "#";
		}
	}

	private class ConnectionListener implements Runnable
	{
		@Override
		public void run()
		{
			Socket buffer = null;

			try
			{
				System.out.println(this + " listening ");
				do
				{
					System.out.println(this + " waiting for client ");
					buffer = Server.this.serverSocket.accept();
					System.out.println(this + " connection attempt ");

					Server.this.welcomeSocket(buffer);

				}
				while (true);
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
		}

		@Override
		public String toString()
		{
			return "#LISTENER" + Test.getThreadName() + "#";
		}
	}

	private class ServerChannel implements Channel
	{
		private ObjectInputStream in = null;
		private ObjectOutputStream out = null;
		private Socket socket = null;
		private String clientName = null;

		public ServerChannel(Socket s)
		{
			this.socket = s;
		}

		public void feed(Object o) throws IOException
		{
			this.out.writeObject(o);
			this.out.flush();
		}

		public void open() throws IOException
		{
			InputStream is = this.socket.getInputStream();
			OutputStream os = this.socket.getOutputStream();

			this.in = new ObjectInputStream(is);
			this.out = new ObjectOutputStream(os);

			System.out.println(this + " waiting for name");
			this.clientName = this.in.readUTF();
		}

		@Override
		public void listen1()
		{
			Object o = null;

			do
			{
				System.out.println(this + " waiting for input");
				try
				{
					o = this.in.readObject();

					System.out.println(this + "Broadcasting \"" + o + "\"");
					Server.this.broadcast(o);
				}
				catch (ClassNotFoundException e)
				{
				}
				catch (IOException e)
				{

				}
				System.out.println(this + " \"" + o + "\" read");
			}
			while (true);
		}

		public void listen(boolean accepted) throws IOException
		{

			System.out.println(this + " answering " + accepted + " to client");
			this.out.writeBoolean(accepted);
			this.out.flush();

			if (accepted)
			{
				System.out.println(this + " connected");

			}
		}

		public String getName()
		{
			return this.clientName;
		}

		@Override
		public String toString()
		{
			return "#SERVER_CHAN:" + this.clientName + Test.getThreadName()
					+ "#";
		}
	}

	private static void startService(Runnable r)
	{
		Thread t = new Thread(r);
		t.start();
		System.out.println("thread " + t.getName() + " started");
	}
}
